From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001
From: Fuwn <50817549+Fuwn@users.noreply.github.com>
Date: Sat, 24 Jan 2026 13:09:50 +0000
Subject: Initial commit

Created from https://vercel.com/new
---
 .../[websiteId]/sessions/SessionProperties.tsx     | 97 ++++++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx

(limited to 'src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx')

diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx
new file mode 100644
index 0000000..1693d05
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx
@@ -0,0 +1,97 @@
+import { Column, Grid, ListItem, Select } from '@umami/react-zen';
+import { useMemo, useState } from 'react';
+import { PieChart } from '@/components/charts/PieChart';
+import { LoadingPanel } from '@/components/common/LoadingPanel';
+import {
+  useMessages,
+  useSessionDataPropertiesQuery,
+  useSessionDataValuesQuery,
+} from '@/components/hooks';
+import { ListTable } from '@/components/metrics/ListTable';
+import { CHART_COLORS } from '@/lib/constants';
+
+export function SessionProperties({ websiteId }: { websiteId: string }) {
+  const [propertyName, setPropertyName] = useState('');
+  const { formatMessage, labels } = useMessages();
+  const { data, isLoading, isFetching, error } = useSessionDataPropertiesQuery(websiteId);
+
+  const properties: string[] = data?.map(e => e.propertyName);
+
+  return (
+    <LoadingPanel
+      isLoading={isLoading}
+      isFetching={isFetching}
+      data={data}
+      error={error}
+      minHeight="300px"
+    >
+      <Column gap="6">
+        {data && (
+          <Grid columns="repeat(auto-fill, minmax(300px, 1fr))" gap>
+            <Select
+              label={formatMessage(labels.event)}
+              value={propertyName}
+              onChange={setPropertyName}
+              placeholder=""
+            >
+              {properties?.map(p => (
+                <ListItem key={p} id={p}>
+                  {p}
+                </ListItem>
+              ))}
+            </Select>
+          </Grid>
+        )}
+        {propertyName && <SessionValues websiteId={websiteId} propertyName={propertyName} />}
+      </Column>
+    </LoadingPanel>
+  );
+}
+
+const SessionValues = ({ websiteId, propertyName }) => {
+  const { data, isLoading, isFetching, error } = useSessionDataValuesQuery(websiteId, propertyName);
+
+  const propertySum = useMemo(() => {
+    return data?.reduce((sum, { total }) => sum + total, 0) ?? 0;
+  }, [data]);
+
+  const chartData = useMemo(() => {
+    if (!propertyName || !data) return null;
+    return {
+      labels: data.map(({ value }) => value),
+      datasets: [
+        {
+          data: data.map(({ total }) => total),
+          backgroundColor: CHART_COLORS,
+          borderWidth: 0,
+        },
+      ],
+    };
+  }, [propertyName, data]);
+
+  const tableData = useMemo(() => {
+    if (!propertyName || !data || propertySum === 0) return [];
+    return data.map(({ value, total }) => ({
+      label: value,
+      count: total,
+      percent: 100 * (total / propertySum),
+    }));
+  }, [propertyName, data, propertySum]);
+
+  return (
+    <LoadingPanel
+      isLoading={isLoading}
+      isFetching={isFetching}
+      data={data}
+      error={error}
+      minHeight="300px"
+    >
+      {data && (
+        <Grid columns="1fr 1fr" gap>
+          <ListTable title={propertyName} data={tableData} />
+          <PieChart type="doughnut" chartData={chartData} />
+        </Grid>
+      )}
+    </LoadingPanel>
+  );
+};
-- 
cgit v1.2.3

